package com.ssd.service.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ObjectUtil;


import com.ssd.api.payment.dto.enums.PayChannelEnum;
import com.ssd.api.payment.dto.enums.TradingStateEnum;
import com.ssd.exception.LogicException;
import com.ssd.constant.Constants;
import com.ssd.constant.TradingCacheConstant;
import com.ssd.constant.TradingConstant;
import com.ssd.enums.TradingEnum;
import com.ssd.handler.BeforePayHandler;
import com.ssd.handler.HandlerFactory;
import com.ssd.handler.NativePayHandler;
import com.ssd.model.domain.Trading;
import com.ssd.service.BasicPayService;
import com.ssd.service.NativePayService;
import com.ssd.service.QRCodeService;
import com.ssd.service.TradingService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Native支付方式Face接口：商户生成二维码，用户扫描支付
 *
 * @author itcast
 */
@Service
@Slf4j
public class NativePayServiceImpl implements NativePayService {

    // 使用@Resource注解注入RedissonClient实例，用于后续的Redis操作
    @Resource
    private RedissonClient redissonClient;

    // 使用@Resource注解注入TradingService实例，用于处理与交易相关的业务逻辑
    @Resource
    private TradingService tradingService;

    // 使用@Resource注解注入BeforePayHandler实例，用于在支付前进行必要的处理操作
    @Resource
    private BeforePayHandler beforePayHandler;

    // 使用@Resource注解注入QRCodeService实例，用于处理与二维码生成或解析相关的操作
    @Resource
    private QRCodeService qrCodeService;

    // 使用@Resource注解注入BasicPayService实例，用于处理基础支付相关的业务逻辑
    @Resource
    private BasicPayService basicPayService;

    /**
     * 切换支付渠道自动关单
     * 当需要更换支付渠道时，此方法会关闭与目标支付渠道不同的、处于支付中状态的交易单
     * @param tradingChannel 要切换的目标支付渠道
     * @param productAppId   业务系统标识
     * @param productOrderNo 业务订单号
     */
    private void changeChannelAndCloseTrading(String productAppId, Long productOrderNo, String tradingChannel) {
        //根据业务订单号和业务标识查询交易单
        List<Trading> yjsTradByProductOrderNo = tradingService.queryByProductOrder(productAppId, productOrderNo);

        yjsTradByProductOrderNo.forEach(v -> {
            //与目标支付渠道不同的渠道且支付中的进行关单
            if (ObjectUtil.notEqual(v.getTradingChannel(), tradingChannel) &&
                    v.getTradingState().equals(TradingStateEnum.FKZ)) {
                //关单
                Boolean result = basicPayService.closeTrading(v.getTradingOrderNo());
                //记录日志
                log.info("业务系统:{},业务订单:{},切换交易订单:{}的支付渠道为:{},关闭其它支付渠道:{}", productAppId, productOrderNo, v.getTradingOrderNo(), tradingChannel, v.getTradingChannel());
            }
        });
    }

    //扫码支付，收银员通过收银台或商户后台调用此接口，生成二维码后，展示给用户，由用户扫描二维码完成订单支付。
    @Override
    public Trading createDownLineTrading(boolean changeChannel, Trading tradingEntity) {
        //获取付款中的记录
        Trading trading = tradingService
                .queryDuringTrading(tradingEntity.getProductAppId(), tradingEntity.getProductOrderNo(), tradingEntity.getTradingChannel());
        //如果切换二维码需要查询其它支付渠道付款中的交易单进行退款操作
        if (changeChannel) {
            changeChannelAndCloseTrading(tradingEntity.getProductAppId(), tradingEntity.getProductOrderNo(), tradingEntity.getTradingChannel());
        }
        //有付款中的记录直接返回无需生成二维码
        if (ObjectUtil.isNotNull(trading)) {
            return trading;
        }
        //交易前置处理：检测交易单参数
        beforePayHandler.checkCreateTrading(tradingEntity);

        //交易类型【付款、退款、免单、挂账】 付款
        tradingEntity.setTradingType(TradingConstant.TRADING_TYPE_FK);
        //是否有效
        tradingEntity.setEnableFlag(Constants.YES);

        //对交易订单加锁
        Long productOrderNo = tradingEntity.getProductOrderNo();
        String key = TradingCacheConstant.CREATE_PAY + productOrderNo;
        RLock lock = redissonClient.getFairLock(key);
        try {
            //获取锁
            if (lock.tryLock(TradingCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)) {
                //交易前置处理：幂等性处理
//                this.beforePayHandler.idempotentCreateTrading(tradingEntity);

                //调用不同的支付渠道进行处理
                PayChannelEnum payChannel = PayChannelEnum.valueOf(tradingEntity.getTradingChannel());
                NativePayHandler nativePayHandler = HandlerFactory.get(payChannel, NativePayHandler.class);
                nativePayHandler.createDownLineTrading(tradingEntity);

                //生成统一收款二维码
                String placeOrderMsg = tradingEntity.getPlaceOrderMsg();
                String qrCode = this.qrCodeService.generate(placeOrderMsg, payChannel);
                tradingEntity.setQrCode(qrCode);
                //指定交易状态为付款中
                tradingEntity.setTradingState(TradingStateEnum.FKZ);
                //新增交易数据
                boolean flag = this.tradingService.save(tradingEntity);
                if (!flag) {
                    throw new LogicException( TradingEnum.SAVE_OR_UPDATE_FAIL.getValue());
                }

                return tradingEntity;
            }
            throw new LogicException( TradingEnum.NATIVE_PAY_FAIL.getValue());
        } catch (LogicException e) {
            throw e;
        } catch (Exception e) {
            log.error("统一收单线下交易预创建异常:{}", ExceptionUtil.stacktraceToString(e));
            throw new LogicException( TradingEnum.NATIVE_PAY_FAIL.getValue()+""+e.getMessage());
        } finally {
            lock.unlock();
        }
    }


}
